# -*- coding: utf8 -*-
"""
根据两点之间的二分法，对两点之间进行打码
"""
from typing import Tuple
from typing import Callable
from math import log2
from math import fabs
from math import ceil
from tool import range_float
from tool import validate_longitude_or_latitude_format
from tool import transform_longitude_or_latitude_into_float
from grid_encoding_level_n import *


def generate_grid_code_from_two_points(point1:Tuple[float, float], point2:Tuple[float, float], grid_encode_function: Callable[[float, float], str], grid_long_size:float, grid_lat_size:float):
    """
    通过二分法打点法，完成两点之间网格的北斗网格
    :param point1: tuple, (longitude_float, latitude_float)
    :param point2: tuple, (longitude_float, latitude_float)
    :param grid_encode_function: func
    :param grid_long_size: int or float, geosot grid size
    :param grid_lat_size: int or float, geosot grid size
    :return: list, grid_code
    """
    (longitude_float1, latitude_float1) = point1
    (longitude_float2, latitude_float2) = point2
    # geosot 切分细力度
    diff_longitude = fabs(longitude_float2 - longitude_float1)
    diff_latitude = fabs(latitude_float2 - latitude_float1)
    n_long = ceil(round(log2(diff_longitude/grid_long_size),10))
    n_lat = ceil(round(log2(diff_latitude/grid_lat_size),10))
    n_max = max(n_long, n_lat)
    delta_long = round(diff_longitude/(2**n_max),12)
    delta_lat = round(diff_latitude/(2**n_max),12)
    # 生成序列
    longitude_float_list = range_float(longitude_float1, longitude_float2, delta_long)
    latitude_float_list = range_float(latitude_float1, latitude_float2, delta_lat)
    grid_code_list = map(lambda x:grid_encode_function(*x), zip(longitude_float_list,latitude_float_list))
    return grid_code_list


def beidou_grid_code_line(longitudeList:str, latitudeList:str, level_n:int) -> list[str]:
    """
    给出平面上一条线的经纬度表示，构造成点。要求经纬度以字符串样式传入，并且逗号分割。经纬度相对应 (long1,lat1),(long2,lat2),(long3,lat3)
    :param longitudeList: str , seperator ; ,demo: "long1;long2;long3"
    :param latitudeList: str , seperator ; ,demo: "lat1;lat2;lat3"
    :param level_n: int , [1,10]
    :return: dict , grid code
    """
    # 经纬度按照 ; 号进行分割
    longitude_list = longitudeList.split(';')
    latitude_ist = latitudeList.split(';')
    try:
        assert len(longitude_list) == len(latitude_ist)
    except:
        raise Exception(f'longitudeList and latitudeList argument length is not equal,please check,{longitudeList}||{latitudeList}')
    else:
        # 验证传入经纬度字符串格式是否正确
        try:
            longitude_format_list = map(validate_longitude_or_latitude_format,longitude_list)
            latitude_format_list = map(validate_longitude_or_latitude_format, latitude_ist)
        except:
            raise Exception(f'longitudeList or latitudeList argument format error,please check,{longitudeList}||{latitudeList}')
        else:
            # 把经纬度转换成小数表示
            longitude_float_list = map(transform_longitude_or_latitude_into_float, longitude_format_list)
            latitude_float_list = map(transform_longitude_or_latitude_into_float, latitude_format_list)
            # 网格编码函数
            grid_func = grid_encoding_function.get(level_n)
            grid_size_long_leveln = eval(f'grid_long_level{level_n}')
            grid_size_lat_leveln = eval(f'grid_lat_level{level_n}')
            # 构造点组单元
            point_float_list = [None] * (len(longitude_list) - 1)
            long_temp = lat_temp = None
            point_idx = 0
            for long, lat in zip(longitude_float_list,latitude_float_list):
                if long_temp is None:
                    long_temp = long
                    lat_temp = lat
                else:
                    point1 = (long_temp, lat_temp)
                    point2 = (long, lat)
                    (long_temp, lat_temp) = (long, lat)
                    func_args = (point1, point2, grid_func, grid_size_long_leveln, grid_size_lat_leveln)
                    point_float_list[point_idx] = func_args
                    point_idx = point_idx + 1
            # 调用两点之间函数值
            grid_encode_result_list = map(lambda x:generate_grid_code_from_two_points(*x), point_float_list)
            grid_code_result_list = [grid_code for result in grid_encode_result_list for grid_code in result]
            # 去除重复值
            length = len(grid_code_result_list)
            grid_code_result_unique_list = [None] * length
            temp_compare = grid_code_result_list[0][0]
            grid_code_result_unique_list[0] = temp_compare
            i = j = 1
            while j < length:
                if temp_compare == grid_code_result_list[j][0]:
                    j = j + 1
                else:
                    grid_code_result_unique_list[i] = grid_code_result_list[j]
                    temp_compare = grid_code_result_list[j][0]
                    i = i + 1
                    j = j + 1
            result = grid_code_result_unique_list[:i]
            return {'succeed':'true','grid_code':result,'longitudeList':longitudeList,'latitudeList':latitudeList}



if __name__ == '__main__':
    longitudeList='E116°18′45.37″;E116°19′45.37″'
    latitudeList='N39°59′35.38″;N39°58′35.38″'
    level_n = 10
    print(beidou_grid_code_line(longitudeList, latitudeList, level_n))
    # 性能测试
    from timeit import timeit
    iter_number = 10
    duration = timeit(lambda: beidou_grid_code_line(longitudeList, latitudeList, level_n), number=iter_number)
    print('generate grid code-{},iter number {},cost time is {} seconds'.format(level_n, iter_number, duration))
    # generate grid code-8,iter number 1000,cost time is 98.7525758000047 seconds
    # generate grid code-9,iter number 100,cost time is 58.044584999996005 seconds
    # generate grid code-10,iter number 10,cost time is 69.56634330000088 seconds
